using System;
using System.Net;
using Subkismet;
using Subkismet.Properties;

namespace Subkismet
{
	/// <summary>
	/// Acts as a client to the Akismet service. Use this class to interface with the 
	/// Akismet service from your web application. For fine grained control, you have 
	/// the option to use the <see cref="AkismetClient" /> class directly.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The constructors require that you supply a delegate used to convert whatever 
	/// representation of a comment you have in your system to one that is understood 
	/// by this system.
	/// </para>
	/// <para>
	/// This provides for loose coupling.
	/// </para>
	/// </remarks>
	/// <typeparam name="TInput">The type of the input.</typeparam>
	[Serializable]
	public class Akismet<TInput>
	{
		IAkismetClient akismet;
		private Converter<TInput, Comment> commentConverter;

		/// <summary>
		/// Creates a simple instance of Akismet for those who plan to simply use the <see cref="Comment" /> 
		/// type in their own code.
		/// </summary>
		/// <param name="apiKey">The API key.</param>
		/// <param name="rootUrl">The root URL.</param>
		/// <returns></returns>
		public static Akismet<Comment> CreateSimpleAkismet(string apiKey, Uri rootUrl)
		{
			return new Akismet<Comment>(apiKey, rootUrl, delegate(Comment comment) { return comment; });
		}

		/// <summary>
		/// Initializes a new instance of the Akismet class.
		/// </summary>
		/// <param name="apiKey">The API key.</param>
		/// <param name="rootUrl">The url of the site hosting and using the Akismet service.</param>
		/// <param name="commentConverter">A delegate used to convert a comment from the host site into one this library understands.</param>
		public Akismet(string apiKey, Uri rootUrl, Converter<TInput, Comment> commentConverter) : this(new AkismetClient(apiKey, rootUrl), commentConverter, null)
		{
		}

		/// <summary>
		/// Initializes a new instance of the Akismet class.
		/// </summary>
		/// <param name="apiKey">The API key.</param>
		/// <param name="rootUrl">The url of the site hosting and using the Akismet service.</param>
		/// <param name="commentConverter">A delegate used to convert a comment from the host site into one this library understands.</param>
		/// <param name="proxy">The proxy.</param>
		public Akismet(string apiKey, Uri rootUrl, Converter<TInput, Comment> commentConverter, IWebProxy proxy)
			: this(new AkismetClient(apiKey, rootUrl), commentConverter, proxy)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="Akismet&lt;TInput&gt;"/> class.
		/// </summary>
		/// <param name="akismetClient">The akismet client.</param>
		/// <param name="commentConverter">A delegate used to convert a comment from the host site into one this library understands.</param>
		public Akismet(IAkismetClient akismetClient, Converter<TInput, Comment> commentConverter) : this(akismetClient, commentConverter, null)
		{
		}

		/// <summary>
		/// Initializes a new instance of the Akismet class.
		/// </summary>
		/// <param name="akismetClient">The akismet client.</param>
		/// <param name="commentConverter">A delegate used to convert a comment from the host site into one this library understands.</param>
		/// <param name="proxy">The proxy to use if any. Leave it null if no proxy is required.</param>
		public Akismet(IAkismetClient akismetClient, Converter<TInput, Comment> commentConverter, IWebProxy proxy)
		{
			if (akismetClient == null)
				throw new ArgumentNullException("akismetClient", Resources.ArgumentNull_Generic);

			if (commentConverter == null)
				throw new ArgumentNullException("commentConverter", Resources.ArgumentNull_Generic);

			this.akismet = akismetClient;

			if (proxy != null)
			{
				this.akismet.Proxy = proxy;
			}

			this.commentConverter = commentConverter;
		}

		/// <summary>
		/// Verifies the api key.
		/// </summary>
		/// <returns></returns>
		public virtual bool VerifyApiKey()
		{
			return this.akismet.VerifyApiKey();
		}

		/// <summary>
		/// Examines the item and determines whether or not it is spam.
		/// </summary>
		/// <exception type="InvalidResponseException">Thrown if the response from Akismet is not what we expected.</exception>
		/// <param name="comment"></param>
		/// <returns></returns>
		public virtual bool IsSpam(TInput comment)
		{
			Comment subkismetComment = ConvertToAkismetItem(comment);
			if (this.akismet.CheckCommentForSpam(subkismetComment))
			{
				this.akismet.SubmitSpam(subkismetComment);
				return true;
			}
			return false;
		}

		/// <summary>
		/// Submits the item to the service as a false positive. 
		/// Something that should not have been marked as spam.
		/// </summary>
		/// <param name="comment"></param>
		public virtual void SubmitGoodFeedback(TInput comment)
		{
			Comment subkismetComment = ConvertToAkismetItem(comment);
			this.akismet.SubmitHam(subkismetComment);
		}

		/// <summary>
		/// Submits the item to the service as a piece of SPAM that got through 
		/// the filter. Something that should've been marked as SPAM.
		/// </summary>
		/// <param name="comment"></param>
		public virtual void SubmitSpam(TInput comment)
		{
			Comment subkismetComment = ConvertToAkismetItem(comment);
			this.akismet.SubmitSpam(subkismetComment);
		}

		private Comment ConvertToAkismetItem(TInput comment)
		{
			return this.commentConverter(comment);
		}
	}
}
